package com.luckberry.murder.service.impl;

import com.alibaba.fastjson.JSON;
import com.luckberry.murder.entity.House;
import com.luckberry.murder.entity.Script;
import com.luckberry.murder.entity.game.Appraise;
import com.luckberry.murder.entity.game.Game;
import com.luckberry.murder.entity.game.Participant;
import com.luckberry.murder.entity.good.Order;
import com.luckberry.murder.mapper.GameMapper;
import com.luckberry.murder.mapper.ScriptMapper;
import com.luckberry.murder.service.GameService;
import com.luckberry.murder.service.HouseService;
import com.luckberry.murder.service.OrderService;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;

@Service
public class GameServiceImpl implements GameService {

    @Resource
    private GameMapper mapper;
    @Resource
    private ScriptMapper scriptMapper;
    @Resource
    private HouseService houseService;
    @Resource
    private OrderService orderService;
    private HashMap<Integer, Timer> tasks;

    @PostConstruct
    public void initTask() {
        tasks = new HashMap<>();
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                getGames().forEach(game -> add(game));
            }
        }, 3000);
    }

    public void add(Game game) {
        if (game == null) return;
        Timer timer = new Timer();
        long end = game.getStart().getTime() + (long) scriptMapper.getScript(game.getSid()).getTime() * 60 * 1000;
        if (end < new Date().getTime()) deleteGame(game.getGid());
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                deleteGame(game.getGid());
            }
        }, new Date(end));
        tasks.put(game.getGid(), timer);
    }

    public void remove(int gid) {
        if (tasks.containsKey(gid)) {
            tasks.get(gid).cancel();
            tasks.remove(gid);
        }
    }

    @Override
    public List<Game> getGames() {
        return mapper.getGames();
    }

    @Override
    public Game getGame(int gid) {
        return mapper.getGame(gid);
    }

    @Override
    public Game getGameByUser(int uid) {
        return mapper.getGameByUser(uid);
    }

    @Override
    public boolean addGame(Game game) {
        boolean result = mapper.addGame(game) == 1;
        add(getGameByUser(game.getUid()));
        Script script = scriptMapper.getScript(game.getSid());
        if (script == null) return false;
        script.setPlayed(script.getPlayed() + 1);
        scriptMapper.editScript(script);
        mapper.addParticipant(Participant.builder()
                .uid(game.getUid())
                .gid(mapper.getGameByUser(game.getUid()).getGid())
                .build());
        House house = houseService.getHouse(game.getHid());
        if (house != null) {
            house.setState("occupied");
            houseService.editHouse(house);
        }
        orderService.addOrder(Order.builder()
                .uid(game.getUid())
                .type("script")
                .price(scriptMapper.getScript(game.getSid()).getPrice())
                .content(JSON.toJSONString(game))
                .count(1)
                .time(new Date())
                .build());
        return result;
    }

    @Override
    public boolean deleteGame(int gid) {
        Game game = mapper.getGame(gid);
        if (game == null) return false;
        House house = houseService.getHouse(game.getHid());
        boolean result = mapper.deleteGame(gid) == 1;
        if (result) {
            house.setState("idle");
            houseService.editHouse(house);
        }
        remove(gid);
        return result;
    }

    @Override
    public boolean deleteGameByUser(int uid) {
        Game game = mapper.getGameByUser(uid);
        if (game == null) return false;
        remove(game.getGid());
        House house = houseService.getHouse(game.getHid());
        if (house == null) return false;
        boolean result = mapper.deleteGameByUser(uid) == 1;
        if (result) {
            house.setState("idle");
            houseService.editHouse(house);
        }
        return result;
    }

    @Override
    public boolean editGame(Game game) {
        Game oldGame = mapper.getGameByUser(game.getUid());
        boolean result = mapper.editGame(game) == 1;
        if (result && oldGame != null) {
            House oldHouse = houseService.getHouse(oldGame.getHid());
            if (oldHouse != null) {
                oldHouse.setState("idle");
                houseService.editHouse(oldHouse);
            }
            House house = houseService.getHouse(game.getHid());
            if (house != null) {
                house.setState("occupied");
                houseService.editHouse(house);
            }
        }
        return result;
    }

    @Override
    public List<Appraise> getAppraises() {
        return mapper.getAppraises();
    }

    @Override
    public List<Appraise> getAppraisesBySid(int sid) {
        return mapper.getAppraisesBySid(sid);
    }

    @Override
    public Appraise getAppraise(int aid) {
        return mapper.getAppraise(aid);
    }

    @Override
    public Appraise getAppraiseByUser(int uid) {
        return mapper.getAppraiseByUser(uid);
    }

    @Override
    public boolean addAppraise(Appraise appraise) {
        Script script = scriptMapper.getScript(appraise.getSid());
        if (script == null) return false;
        script.setStar(script.getStar() + appraise.getStar());
        script.setStarPeople(script.getStarPeople() + 1);
        scriptMapper.editScript(script);
        return mapper.addAppraise(appraise) == 1;
    }

    @Override
    public boolean deleteAppraise(int aid) {
        Appraise appraise = mapper.getAppraise(aid);
        if (appraise == null) return false;
        Script script = scriptMapper.getScript(appraise.getSid());
        if (script == null) return false;
        script.setStar(Math.max(0, script.getStar() - appraise.getStar()));
        script.setStarPeople(Math.max(0, script.getStarPeople() - 1));
        scriptMapper.editScript(script);
        return mapper.deleteAppraise(aid) == 1;
    }

    @Override
    public boolean editAppraise(Appraise appraise) {
        Appraise old = mapper.getAppraise(appraise.getAid());
        if (old == null) return false;
        Script script = scriptMapper.getScript(old.getSid());
        boolean result = mapper.editAppraise(appraise) == 1;
        script.setStar(script.getStar() - old.getStar() + (result ? appraise.getStar() : 0));
        scriptMapper.editScript(script);
        return result;
    }

    @Override
    public List<Participant> getParticipants() {
        return mapper.getParticipants();
    }

    @Override
    public List<Participant> getParticipantsByGame(int gid) {
        return mapper.getParticipantsByGame(gid);
    }

    @Override
    public Participant getParticipantByUser(int uid) {
        return mapper.getParticipantByUser(uid);
    }

    @Override
    public boolean isJoin(Participant participant) {
        return mapper.isJoin(participant) != null;
    }

    @Override
    public boolean addParticipant(Participant participant) {
        if (isJoin(participant)) return false;
        Game game = mapper.getGame(participant.getGid());
        Script script = scriptMapper.getScript(game.getSid());
        if (script == null || game.getPeople() >= script.getPeople()) return false;
        game.setPeople(game.getPeople() + 1);
        mapper.editGame(game);
        script.setPlayed(script.getPlayed() + 1);
        scriptMapper.editScript(script);
        return mapper.addParticipant(participant) == 1;
    }

    @Override
    public boolean deleteParticipantByUser(int uid) {
        Participant participant = mapper.getParticipantByUser(uid);
        if (participant == null) return false;
        Game game = mapper.getGame(participant.getGid());
        if (game == null) return false;
        game.setPeople(Math.max(1, game.getPeople() - 1));
        mapper.editGame(game);
        return mapper.deleteParticipantByUser(uid) == 1;
    }

    @Override
    public boolean deleteParticipantByGame(int gid) {
        Game game = mapper.getGame(gid);
        if (game == null) return false;
        game.setPeople(Math.max(1, game.getPeople() - getParticipantsByGame(gid).size()));
        mapper.editGame(game);
        return mapper.deleteParticipantByGame(gid) == 1;
    }
}
